/**
 * ****************************************************************************
 * Project: RoboBridge
 * ----------------------------------------------------------------------------
 * -------------------------- [c]2012 - KrawallMann ---------------------------
 * ---------------------- http://www.krawallmann.de.tc/ -----------------------
 * ****************************************************************************
 * File: RoboView.java Package: tc.de.krawallmann.robobridge Version: 1.0
 * Author(s): KrawallMann
 * ****************************************************************************
 * Description:
 * ****************************************************************************
 */
package tc.de.krawallmann.robobridge;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;

import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.UIManager;

import tc.de.krawallmann.robobridge.communication.AbstractCommunicationInterface;
import tc.de.krawallmann.robobridge.communication.ConnectionListener;
import tc.de.krawallmann.robobridge.communication.DeviceDiscoveryListener;
import tc.de.krawallmann.robobridge.communication.lan.wlan.RSSIListener;
import tc.de.krawallmann.robobridge.communication.lan.wlan.WLANRemoteDevice;
import tc.de.krawallmann.robobridge.devices.AbstractDevice;
import tc.de.krawallmann.robobridge.devices.SensorListener;
import tc.de.krawallmann.robobridge.devices.implementation.DrivingLights;
import tc.de.krawallmann.robobridge.devices.implementation.M256;
import tc.de.krawallmann.robobridge.devices.implementation.RNControl;
import tc.de.krawallmann.robobridge.devices.implementation.RP6Base;
import tc.de.krawallmann.robobridge.devices.implementation.SRF08;
import tc.de.krawallmann.robobridge.devices.implementation.SRF08OnServo;
import tc.de.krawallmann.robobridge.util.Log;
import tc.de.krawallmann.robobridge.visualization.AbstractRoboViewDisplay;
import tc.de.krawallmann.robobridge.visualization.BaseFrame;
import tc.de.krawallmann.robobridge.visualization.CMPS03Frame;
import tc.de.krawallmann.robobridge.visualization.ConnectFrame;
import tc.de.krawallmann.robobridge.visualization.DrivingLightsFrame;
import tc.de.krawallmann.robobridge.visualization.FixedRadar;
import tc.de.krawallmann.robobridge.visualization.M256Frame;
import tc.de.krawallmann.robobridge.visualization.RNControlFrame;
import tc.de.krawallmann.robobridge.visualization.RemoteControl;
import tc.de.krawallmann.robobridge.visualization.SRF08OnServoFrame;
import tc.de.krawallmann.robobridge.visualization.WelcomeFrame;
import eu.hansolo.steelseries.extras.Compass;
import eu.hansolo.steelseries.gauges.SparkLine;
import eu.hansolo.steelseries.tools.SmoothingFunction;

/**
 * The Class RoboView will produce a GUI for the user to interact with the
 * robot.
 * 
 * @author KrawallMann
 * @version 1.0
 */
public class RoboView extends JFrame implements DeviceDiscoveryListener<WLANRemoteDevice>,
		SensorListener, RSSIListener, ConnectionListener<WLANRemoteDevice> {

	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 2038669249586415634L;
	private static final Object RP6Base = null;
	/** The bridge. */
	private transient final RoboBridge<WLANRemoteDevice> bridge;

	/** The compass. */
	private JInternalFrame compass;

	/** The connect frame. */
	private final ConnectFrame connectFrame;

	/** The desktop. */
	private final JDesktopPane desktop;

	/** The file. */
	private final JMenu file;

	/** The menu. */
	private final JMenuBar menu;

	/** The radar. */
	private FixedRadar radar;

	/** The rc. */
	private RemoteControl rc;
	/** The view. */
	private final JMenu view;

	private Map<AbstractDevice, AbstractRoboViewDisplay> displays =
			new HashMap<AbstractDevice, AbstractRoboViewDisplay>();

	/**
	 * Instantiates a new robo view.
	 * 
	 * @param bridge
	 *            the bridge
	 */
	public RoboView(final RoboBridge<WLANRemoteDevice> bridge) {
		super("RoboBridge::RoboView");

		this.bridge = bridge;
		bridge.getCommunicationInterface().getDeviceScanner().addDeviceDiscoveryListener(this);
		bridge.getCommunicationInterface().addConnectionListener(this);

		for (final AbstractDevice d : bridge.getRobot().getDevices()) {
			d.addSensorListener(this);
		}
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		menu = new JMenuBar();
		file = new JMenu("File");
		view = new JMenu("View");

		for (AbstractDevice d : bridge.getRobot().getDevices()) {
			view.add(createMenuEntryFor(d));
		}
		JMenuItem rccontrol = new JMenuItem("Show RC");
		rccontrol.addActionListener(new ActionListener() {
			
			private RemoteControl rc;

			@Override
			public void actionPerformed(ActionEvent arg0) {
				if(rc!=null&&rc.isDisplayable()){
					rc.setVisible(true);
					rc.moveToFront();
					return;
				}
				rc=new RemoteControl(bridge);
				desktop.add(rc);
				rc.moveToFront();
			}
		});
		view.add(rccontrol);
		menu.add(file);
		menu.add(view);

		setJMenuBar(menu);
		setExtendedState(Frame.MAXIMIZED_BOTH);
		setLayout(new BorderLayout());
		connectFrame = new ConnectFrame(bridge);

		desktop = new JDesktopPane();
		desktop.setBackground(Color.decode("#000066"));

		desktop.add(new WelcomeFrame());
		desktop.add(connectFrame);
		connectFrame.setLocation(400, 0);

		setVisible(true);

		setContentPane(desktop);
	}

	private JMenuItem createMenuEntryFor(final AbstractDevice d) {
		if (d instanceof RP6Base) {
			JMenuItem mi = new JMenuItem("Show " + d.getModel() + " @" + d.getDeviceId());
			mi.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent arg0) {
					if (displays.containsKey(d) && displays.get(d).isDisplayable()) {
						displays.get(d).setVisible(true);
						displays.get(d).moveToFront();
						return;
					}
					AbstractRoboViewDisplay fr = new BaseFrame((RP6Base) d);
					displays.put(d, fr);
					desktop.add(fr);
					fr.moveToFront();
				}
			});
			return mi;
		}

		if (d instanceof SRF08OnServo) {
			final JMenuItem mi = new JMenuItem("Show " + d.getModel() + " @" + d.getDeviceId());
			mi.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent arg0) {
					if (displays.containsKey(d) && displays.get(d).isDisplayable()) {
						displays.get(d).setVisible(true);
						displays.get(d).moveToFront();
						return;
					}
					AbstractRoboViewDisplay fr = new SRF08OnServoFrame((SRF08OnServo) d);
					displays.put(d, fr);
					desktop.add(fr);
					fr.moveToFront();
				}
			});
			return mi;
		}

		if (d instanceof SRF08) {
			final JMenuItem mi = new JMenuItem("Show " + d.getModel() + " @" + d.getDeviceId());
			mi.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent arg0) {
					if (displays.containsKey(d) && displays.get(d).isDisplayable()) {
						displays.get(d).setVisible(true);
						displays.get(d).moveToFront();
						return;
					}
					AbstractRoboViewDisplay fr = null;// TODO new SRF08Frame((DEVICE_ID_SRF08)d);
					displays.put(d, fr);
					desktop.add(fr);
					fr.moveToFront();

				}
			});
			return mi;
		}
		if (d instanceof RNControl) {
			final JMenuItem mi = new JMenuItem("Show " + d.getModel() + " @" + d.getDeviceId());
			mi.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent arg0) {
					if (displays.containsKey(d) && displays.get(d).isDisplayable()) {
						displays.get(d).setVisible(true);
						displays.get(d).moveToFront();
						return;
					}
					AbstractRoboViewDisplay fr = new RNControlFrame((RNControl) d);
					displays.put(d, fr);
					desktop.add(fr);
					fr.moveToFront();
				}
			});
			return mi;
		}
		
		if (d instanceof M256) {
			final JMenuItem mi = new JMenuItem("Show " + d.getModel() + " @" + d.getDeviceId());
			mi.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent arg0) {
					if (displays.containsKey(d) && displays.get(d).isDisplayable()) {
						displays.get(d).setVisible(true);
						displays.get(d).moveToFront();
						return;
					}
					AbstractRoboViewDisplay fr = new M256Frame(bridge, (M256) d);
					displays.put(d, fr);
					desktop.add(fr);
					fr.moveToFront();
				}
			});
			return mi;
		}
		
		if (d instanceof DrivingLights) {
			final JMenuItem mi = new JMenuItem("Show " + d.getModel() + " @" + d.getDeviceId());
			mi.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent arg0) {
					if (displays.containsKey(d) && displays.get(d).isDisplayable()) {
						displays.get(d).setVisible(true);
						displays.get(d).moveToFront();
						return;
					}
					AbstractRoboViewDisplay fr = new DrivingLightsFrame((DrivingLights) d);
					displays.put(d, fr);
					desktop.add(fr);
					fr.moveToFront();
				}
			});
			return mi;
		}
		return new JMenuItem("Not supported: " + d.getModel());
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.ConnectionListener#onConnect
	 * (tc
	 * .de.krawallmann.robobridge.communication.AbstractRemoteCommunicationDevice
	 * )
	 */
	@Override
	public void onConnect(final WLANRemoteDevice device) {
		Log.log(Level.INFO, getClass(), "onConnect", "Connected to {0}:{1}, RSSI: {2}dBm",
				device.getAddress(), device.getPort(), device.getRssi());
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.robobridge.communication.DeviceDiscoveryListener#
	 * onDeviceDiscovered
	 * (tc.de.krawallmann.robobridge.communication.AbstractRemoteCommunicationDevice
	 * )
	 */
	@Override
	public void onDeviceDiscovered(final WLANRemoteDevice device) {
		if (bridge.getCommunicationInterface().getDeviceScanner().isKnownDevice(device)) {
			Log.log(Level.INFO,
					getClass(),
					"onDeviceDiscovered",
					"Received Broadcast from known device (Probably RP6) at {0}:{1}, device name {2}, RSSI {3}dBm",
					device.getAddress(), device.getPort(), device.getName(), device.getRssi());
		}
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.ConnectionListener#onDisconnect
	 * (
	 * tc.de.krawallmann.robobridge.communication.AbstractRemoteCommunicationDevice
	 * )
	 */
	@Override
	public void onDisconnect(final WLANRemoteDevice device, int reason) {
		if(reason==AbstractCommunicationInterface.ERROR){
			JOptionPane.showInternalMessageDialog(desktop, "The connection to the robot was interrupted!", "Connection lost", JOptionPane.ERROR_MESSAGE);
		}
		Log.log(Level.INFO, getClass(), "onDisconnect", "Disconnected!");
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.robobridge.communication.lan.wlan.RSSIListener#
	 * onRSSIUpdated(int)
	 */
	@Override
	public void onRSSIUpdated(final int rssi) {
		Log.log(Level.INFO, getClass(), "onRSSIUpdated", "RSSI: {3}dBm", rssi);
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.devices.SensorListener#onSensorValueChanged
	 * (tc.de.krawallmann.robobridge.devices.AbstractDevice, java.lang.String,
	 * java.lang.Object, java.lang.Object)
	 */
	@Override
	public void onSensorValueChanged(final AbstractDevice sensor, final String propertyName,
			final Object oldValue, final Object newValue) {
		for (final JInternalFrame frame : desktop.getAllFrames()) {
			if (frame instanceof AbstractRoboViewDisplay) {
				((AbstractRoboViewDisplay) frame).onSensorValueChanged(sensor, propertyName,
						oldValue, newValue);
			}
		}
	}

	/*
	 * /** Creates the cmps frame. protected void createCMPSFrame() { if
	 * (compass != null && compass.isDisplayable()) { compass.setVisible(true);
	 * compass.moveToFront(); return; } compass = new CMPS03Frame();
	 * desktop.add(compass); compass.moveToFront(); } /** Creates the rc frame.
	 * protected void createRCFrame() { if (rc != null && rc.isDisplayable()) {
	 * rc.setVisible(true); rc.moveToFront(); return; } rc = new
	 * RemoteControl(bridge); desktop.add(rc); rc.moveToFront(); } /** Creates
	 * the srf frame. protected void createSRFFrame() { if (srf != null &&
	 * srf.isDisplayable()) { srf.setVisible(true); srf.moveToFront(); return; }
	 * srf = new SRF08OnServoFrame(bridge); desktop.add(srf); srf.moveToFront();
	 * }
	 */

}
